Mono-only terminals are from another century.
[apps/madmutt.git] / lib-ui / color.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9
10 #include <lib-ui/lib-ui.h>
11 #include "pattern.h"
12 #include "mutt.h"
13 #include "madtty.h"
14
15 /* globals */
16 int *ColorQuote;
17 int ColorQuoteUsed;
18 int ColorDefs[MT_COLOR_MAX];
19 COLOR_LINE *ColorHdrList = NULL;
20 COLOR_LINE *ColorBodyList = NULL;
21 COLOR_LINE *ColorIndexList = NULL;
22
23 /* local to this file */
24 static int ColorQuoteSize;
25
26 #define COLOR_DEFAULT (-2)
27
28 typedef struct color_list {
29   short fg;
30   short bg;
31   short index;
32   short count;
33   struct color_list *next;
34 } COLOR_LIST;
35
36 static struct mapping_t Colors[] = {
37   {"black", COLOR_BLACK},
38   {"blue", COLOR_BLUE},
39   {"cyan", COLOR_CYAN},
40   {"green", COLOR_GREEN},
41   {"magenta", COLOR_MAGENTA},
42   {"red", COLOR_RED},
43   {"white", COLOR_WHITE},
44   {"yellow", COLOR_YELLOW},
45   {"default", COLOR_DEFAULT},
46   {0, 0}
47 };
48
49 static struct mapping_t Fields[] = {
50   {"hdrdefault", MT_COLOR_HDEFAULT},
51   {"quoted", MT_COLOR_QUOTED},
52   {"signature", MT_COLOR_SIGNATURE},
53   {"indicator", MT_COLOR_INDICATOR},
54   {"status", MT_COLOR_STATUS},
55   {"tree", MT_COLOR_TREE},
56   {"error", MT_COLOR_ERROR},
57   {"normal", MT_COLOR_NORMAL},
58   {"tilde", MT_COLOR_TILDE},
59   {"markers", MT_COLOR_MARKERS},
60   {"header", MT_COLOR_HEADER},
61   {"body", MT_COLOR_BODY},
62   {"message", MT_COLOR_MESSAGE},
63   {"attachment", MT_COLOR_ATTACHMENT},
64   {"search", MT_COLOR_SEARCH},
65   {"bold", MT_COLOR_BOLD},
66   {"underline", MT_COLOR_UNDERLINE},
67   {"index", MT_COLOR_INDEX},
68   {"sidebar_new", MT_COLOR_NEW},
69   {"sidebar", MT_COLOR_SIDEBAR},
70   {"sidebar_flagged", MT_COLOR_FLAGGED},
71   {NULL, 0}
72 };
73
74 #define COLOR_QUOTE_INIT        8
75
76 static COLOR_LINE *mutt_new_color_line (void)
77 {
78   COLOR_LINE *p = p_new(COLOR_LINE, 1);
79
80   p->fg = p->bg = -1;
81
82   return (p);
83 }
84
85 static void mutt_free_color_line(COLOR_LINE ** l)
86 {
87   COLOR_LINE *tmp;
88
89   if (!l || !*l)
90     return;
91   tmp = *l;
92
93   /* we should really use the container type for regular expressions. */
94   regfree (&tmp->rx);
95   pattern_list_wipe(&tmp->color_pattern);
96   p_delete(&tmp->pattern);
97   p_delete(l);
98 }
99
100 void ci_start_color (void)
101 {
102     memset(ColorDefs, A_NORMAL, sizeof(int) * MT_COLOR_MAX);
103     ColorQuote = p_new(int, COLOR_QUOTE_INIT);
104     memset(ColorQuote, A_NORMAL, sizeof(int) * COLOR_QUOTE_INIT);
105     ColorQuoteSize = COLOR_QUOTE_INIT;
106     ColorQuoteUsed = 0;
107
108     /* set some defaults */
109     ColorDefs[MT_COLOR_STATUS] = A_REVERSE;
110     ColorDefs[MT_COLOR_INDICATOR] = A_REVERSE;
111     ColorDefs[MT_COLOR_SEARCH] = A_REVERSE;
112     ColorDefs[MT_COLOR_MARKERS] = A_REVERSE;
113     /* special meaning: toggle the relevant attribute */
114     ColorDefs[MT_COLOR_BOLD] = 0;
115     ColorDefs[MT_COLOR_UNDERLINE] = 0;
116 }
117
118 static int
119 parse_color_name (const char *s, int *col, int *attr, int brite, BUFFER * err)
120 {
121   char *eptr;
122
123   if (m_strncasecmp(s, "bright", 6) == 0) {
124     *attr |= brite;
125     s += 6;
126   }
127
128   /* allow aliases for xterm color resources */
129   if (m_strncasecmp(s, "color", 5) == 0) {
130     s += 5;
131     *col = strtol (s, &eptr, 10);
132     if (!*s || *eptr || *col < 0 ||
133         (*col >= COLORS && !option (OPTNOCURSES) && has_colors ())) {
134       snprintf (err->data, err->dsize, _("%s: color not supported by term"),
135                 s);
136       return (-1);
137     }
138   }
139   else if ((*col = mutt_getvaluebyname (s, Colors)) == -1) {
140     snprintf (err->data, err->dsize, _("%s: no such color"), s);
141     return (-1);
142   }
143
144   return 0;
145 }
146
147 /* usage: uncolor index pattern [pattern...] */
148 int mutt_parse_uncolor(BUFFER *buf, BUFFER *s, unsigned long data, BUFFER
149                        *err)
150 {
151   int object = 0, do_cache = 0;
152   COLOR_LINE *tmp, *last = NULL;
153
154   mutt_extract_token (buf, s, 0);
155
156   if ((object = mutt_getvaluebyname (buf->data, Fields)) == -1) {
157     snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
158     return (-1);
159   }
160
161   if (m_strncmp(buf->data, "index", 5) != 0) {
162     snprintf (err->data, err->dsize,
163               _("%s: command valid only for index object"), "uncolor");
164     return (-1);
165   }
166
167   if (!MoreArgs (s)) {
168     snprintf (err->data, err->dsize, _("%s: too few arguments"), "uncolor");
169     return (-1);
170   }
171
172   if (option(OPTNOCURSES) || !has_colors()) {
173     /* just eat the command, but don't do anything real about it */
174     do {
175       mutt_extract_token (buf, s, 0);
176     } while (MoreArgs(s));
177     return 0;
178   }
179
180   do {
181     mutt_extract_token (buf, s, 0);
182     if (!m_strcmp("*", buf->data)) {
183       for (tmp = ColorIndexList; tmp;) {
184         if (!do_cache)
185           do_cache = 1;
186         last = tmp;
187         tmp = tmp->next;
188         mutt_free_color_line(&last);
189       }
190       ColorIndexList = NULL;
191     }
192     else {
193       for (last = NULL, tmp = ColorIndexList; tmp;
194            last = tmp, tmp = tmp->next) {
195         if (!m_strcmp(buf->data, tmp->pattern)) {
196           if (!do_cache)
197             do_cache = 1;
198           if (last)
199             last->next = tmp->next;
200           else
201             ColorIndexList = tmp->next;
202           mutt_free_color_line(&tmp);
203           break;
204         }
205       }
206     }
207   }
208   while (MoreArgs (s));
209
210
211   if (do_cache && !option (OPTNOCURSES)) {
212     int i;
213
214     set_option (OPTFORCEREDRAWINDEX);
215     /* force re-caching of index colors */
216     for (i = 0; Context && i < Context->msgcount; i++)
217       Context->hdrs[i]->pair = 0;
218   }
219   return (0);
220 }
221
222
223 static int
224 add_pattern (COLOR_LINE ** top, const char *s, int sensitive,
225              int fg, int bg, int attr, BUFFER * err, int is_index)
226 {
227
228   /* is_index used to store compiled pattern
229    * only for `index' color object 
230    * when called from mutt_parse_color() */
231
232   COLOR_LINE *tmp = *top;
233
234   while (tmp) {
235     if (sensitive) {
236       if (m_strcmp(s, tmp->pattern) == 0)
237         break;
238     }
239     else {
240       if (m_strcasecmp(s, tmp->pattern) == 0)
241         break;
242     }
243     tmp = tmp->next;
244   }
245
246   if (tmp) {
247     if (fg != -1 && bg != -1) {
248       if (tmp->fg != fg || tmp->bg != bg) {
249         tmp->fg = fg;
250         tmp->bg = bg;
251         attr |= madtty_color_pair(fg, bg);
252       } else {
253         attr |= (tmp->pair & ~A_BOLD);
254       }
255     }
256     tmp->pair = attr;
257   } else {
258     int r;
259     char buf[STRING];
260
261     tmp = mutt_new_color_line ();
262     if (is_index) {
263       int i;
264
265       m_strcpy(buf, sizeof(buf), NONULL(s));
266       mutt_check_simple (buf, sizeof (buf), NONULL (SimpleSearch));
267       if ((tmp->color_pattern =
268            mutt_pattern_comp (buf, M_FULL_MSG, err)) == NULL) {
269         mutt_free_color_line(&tmp);
270         return -1;
271       }
272       /* force re-caching of index colors */
273       for (i = 0; Context && i < Context->msgcount; i++)
274         Context->hdrs[i]->pair = 0;
275     }
276     else
277       if ((r =
278            REGCOMP (&tmp->rx, s,
279                     (sensitive ? mutt_which_case (s) : REG_ICASE))) != 0) {
280       regerror (r, &tmp->rx, err->data, err->dsize);
281       mutt_free_color_line(&tmp);
282       return (-1);
283     }
284     tmp->next = *top;
285     tmp->pattern = m_strdup(s);
286     if (fg != -1 && bg != -1) {
287       tmp->fg = fg;
288       tmp->bg = bg;
289       attr |= madtty_color_pair(fg, bg);
290     }
291     tmp->pair = attr;
292     *top = tmp;
293   }
294
295   return 0;
296 }
297
298 static int
299 parse_object (BUFFER * buf, BUFFER * s, int *o, int *ql, BUFFER * err)
300 {
301   int q_level = 0;
302   char *eptr;
303
304   if (!MoreArgs (s)) {
305     m_strcpy(err->data, err->dsize, _("Missing arguments."));
306     return -1;
307   }
308
309   mutt_extract_token (buf, s, 0);
310   if (!m_strncmp(buf->data, "quoted", 6)) {
311     if (buf->data[6]) {
312       *ql = strtol (buf->data + 6, &eptr, 10);
313       if (*eptr || q_level < 0) {
314         snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
315         return -1;
316       }
317     }
318     else
319       *ql = 0;
320
321     *o = MT_COLOR_QUOTED;
322   }
323   else if ((*o = mutt_getvaluebyname (buf->data, Fields)) == -1) {
324     snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
325     return (-1);
326   }
327
328   return 0;
329 }
330
331 typedef int (*parser_callback_t) (BUFFER *, BUFFER *, int *, int *, int *,
332                                   BUFFER *);
333
334 static int
335 parse_color_pair (BUFFER * buf, BUFFER * s, int *fg, int *bg, int *attr,
336                   BUFFER * err)
337 {
338   if (!MoreArgs (s)) {
339     m_strcpy(err->data, err->dsize, _("color: too few arguments"));
340     return (-1);
341   }
342
343   mutt_extract_token (buf, s, 0);
344
345   if (parse_color_name (buf->data, fg, attr, A_BOLD, err) != 0)
346     return (-1);
347
348   if (!MoreArgs (s)) {
349     m_strcpy(err->data, err->dsize, _("color: too few arguments"));
350     return (-1);
351   }
352
353   mutt_extract_token (buf, s, 0);
354
355   if (parse_color_name (buf->data, bg, attr, A_BLINK, err) != 0)
356     return (-1);
357
358   return 0;
359 }
360
361 #if 0
362 static int
363 parse_attr_spec (BUFFER * buf, BUFFER * s, int *fg, int *bg, int *attr,
364                  BUFFER * err)
365 {
366
367   if (fg)
368     *fg = -1;
369   if (bg)
370     *bg = -1;
371
372   if (!MoreArgs (s)) {
373     m_strcpy(err->data, err->dsize, _("mono: too few arguments"));
374     return (-1);
375   }
376
377   mutt_extract_token (buf, s, 0);
378
379   if (ascii_strcasecmp ("bold", buf->data) == 0)
380     *attr |= A_BOLD;
381   else if (ascii_strcasecmp ("underline", buf->data) == 0)
382     *attr |= A_UNDERLINE;
383   else if (ascii_strcasecmp ("none", buf->data) == 0)
384     *attr = A_NORMAL;
385   else if (ascii_strcasecmp ("reverse", buf->data) == 0)
386     *attr |= A_REVERSE;
387   else if (ascii_strcasecmp ("standout", buf->data) == 0)
388     *attr |= A_STANDOUT;
389   else if (ascii_strcasecmp ("normal", buf->data) == 0)
390     *attr = A_NORMAL;           /* needs use = instead of |= to clear other bits */
391   else {
392     snprintf (err->data, err->dsize, _("%s: no such attribute"), buf->data);
393     return (-1);
394   }
395
396   return 0;
397 }
398 #endif
399
400 static int fgbgattr_to_color (int fg, int bg, int attr)
401 {
402   if (fg != -1 && bg != -1)
403     return attr | madtty_color_pair(fg, bg);
404   else
405     return attr;
406 }
407
408 /* usage: color <object> <fg> <bg> [ <regexp> ] */
409 int mutt_parse_color(BUFFER *buf, BUFFER *s, unsigned long i, BUFFER *err)
410 {
411   int object = 0, attr = 0, fg = 0, bg = 0, q_level = 0;
412   int r = 0;
413   int dry_run = option (OPTNOCURSES) || !has_colors ();
414
415   if (parse_object (buf, s, &object, &q_level, err) == -1)
416     return -1;
417
418   if (parse_color_pair(buf, s, &fg, &bg, &attr, err) == -1)
419     return -1;
420
421   /* extract a regular expression if needed */
422
423   if (object == MT_COLOR_HEADER || object == MT_COLOR_BODY
424       || object == MT_COLOR_INDEX) {
425     if (!MoreArgs (s)) {
426       m_strcpy(err->data, err->dsize, _("too few arguments"));
427       return (-1);
428     }
429
430     mutt_extract_token (buf, s, 0);
431   }
432
433   if (MoreArgs (s)) {
434     m_strcpy(err->data, err->dsize, _("too many arguments"));
435     return (-1);
436   }
437
438   /* dry run? */
439
440   if (dry_run)
441     return 0;
442
443
444   if (!option (OPTNOCURSES) && has_colors ()
445       /* delay use_default_colors() until needed, since it initializes things */
446       && (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT)
447       && use_default_colors () != OK) {
448     m_strcpy(err->data, err->dsize, _("default colors not supported"));
449     return (-1);
450   }
451
452   if (object == MT_COLOR_HEADER)
453     r = add_pattern (&ColorHdrList, buf->data, 0, fg, bg, attr, err, 0);
454   else if (object == MT_COLOR_BODY)
455     r = add_pattern (&ColorBodyList, buf->data, 1, fg, bg, attr, err, 0);
456   else if (object == MT_COLOR_INDEX) {
457     r = add_pattern (&ColorIndexList, buf->data, 1, fg, bg, attr, err, 1);
458     set_option (OPTFORCEREDRAWINDEX);
459   }
460   else if (object == MT_COLOR_QUOTED) {
461     if (q_level >= ColorQuoteSize) {
462       p_realloc(&ColorQuote, ColorQuoteSize += 2);
463       ColorQuote[ColorQuoteSize - 2] = ColorDefs[MT_COLOR_QUOTED];
464       ColorQuote[ColorQuoteSize - 1] = ColorDefs[MT_COLOR_QUOTED];
465     }
466     if (q_level >= ColorQuoteUsed)
467       ColorQuoteUsed = q_level + 1;
468     if (q_level == 0) {
469       ColorDefs[MT_COLOR_QUOTED] = fgbgattr_to_color (fg, bg, attr);
470
471       ColorQuote[0] = ColorDefs[MT_COLOR_QUOTED];
472       for (q_level = 1; q_level < ColorQuoteUsed; q_level++) {
473         if (ColorQuote[q_level] == A_NORMAL)
474           ColorQuote[q_level] = ColorDefs[MT_COLOR_QUOTED];
475       }
476     }
477     else
478       ColorQuote[q_level] = fgbgattr_to_color (fg, bg, attr);
479   }
480   else
481     ColorDefs[object] = fgbgattr_to_color (fg, bg, attr);
482
483   if (object == MT_COLOR_NORMAL && !option (OPTNOCURSES) && has_colors ())
484     BKGDSET(main_w, MT_COLOR_NORMAL);
485
486   return (r);
487 }