756e29995b61748c2466ae2d12e35dbec851ded2
[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     if (m_strncasecmp(s, "bright", 6) == 0) {
122         *attr |= brite;
123         s += 6;
124     }
125
126     if ((*col = mutt_getvaluebyname (s, Colors)) == -1) {
127         snprintf (err->data, err->dsize, _("%s: no such color"), s);
128         return (-1);
129     }
130
131     return 0;
132 }
133
134 /* usage: uncolor index pattern [pattern...] */
135 int mutt_parse_uncolor(BUFFER *buf, BUFFER *s, unsigned long data, BUFFER
136                        *err)
137 {
138   int object = 0, do_cache = 0;
139   COLOR_LINE *tmp, *last = NULL;
140
141   mutt_extract_token (buf, s, 0);
142
143   if ((object = mutt_getvaluebyname (buf->data, Fields)) == -1) {
144     snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
145     return (-1);
146   }
147
148   if (m_strncmp(buf->data, "index", 5) != 0) {
149     snprintf (err->data, err->dsize,
150               _("%s: command valid only for index object"), "uncolor");
151     return (-1);
152   }
153
154   if (!MoreArgs (s)) {
155     snprintf (err->data, err->dsize, _("%s: too few arguments"), "uncolor");
156     return (-1);
157   }
158
159   if (option(OPTNOCURSES)) {
160     /* just eat the command, but don't do anything real about it */
161     do {
162       mutt_extract_token (buf, s, 0);
163     } while (MoreArgs(s));
164     return 0;
165   }
166
167   do {
168     mutt_extract_token (buf, s, 0);
169     if (!m_strcmp("*", buf->data)) {
170       for (tmp = ColorIndexList; tmp;) {
171         if (!do_cache)
172           do_cache = 1;
173         last = tmp;
174         tmp = tmp->next;
175         mutt_free_color_line(&last);
176       }
177       ColorIndexList = NULL;
178     }
179     else {
180       for (last = NULL, tmp = ColorIndexList; tmp;
181            last = tmp, tmp = tmp->next) {
182         if (!m_strcmp(buf->data, tmp->pattern)) {
183           if (!do_cache)
184             do_cache = 1;
185           if (last)
186             last->next = tmp->next;
187           else
188             ColorIndexList = tmp->next;
189           mutt_free_color_line(&tmp);
190           break;
191         }
192       }
193     }
194   }
195   while (MoreArgs (s));
196
197
198   if (do_cache && !option (OPTNOCURSES)) {
199     int i;
200
201     set_option (OPTFORCEREDRAWINDEX);
202     /* force re-caching of index colors */
203     for (i = 0; Context && i < Context->msgcount; i++)
204       Context->hdrs[i]->pair = 0;
205   }
206   return (0);
207 }
208
209
210 static int
211 add_pattern (COLOR_LINE ** top, const char *s, int sensitive,
212              int fg, int bg, int attr, BUFFER * err, int is_index)
213 {
214
215   /* is_index used to store compiled pattern
216    * only for `index' color object 
217    * when called from mutt_parse_color() */
218
219   COLOR_LINE *tmp = *top;
220
221   while (tmp) {
222     if (sensitive) {
223       if (m_strcmp(s, tmp->pattern) == 0)
224         break;
225     }
226     else {
227       if (m_strcasecmp(s, tmp->pattern) == 0)
228         break;
229     }
230     tmp = tmp->next;
231   }
232
233   if (tmp) {
234     if (fg != -1 && bg != -1) {
235       if (tmp->fg != fg || tmp->bg != bg) {
236         tmp->fg = fg;
237         tmp->bg = bg;
238         attr |= madtty_color_pair(fg, bg);
239       } else {
240         attr |= (tmp->pair & ~A_BOLD);
241       }
242     }
243     tmp->pair = attr;
244   } else {
245     int r;
246     char buf[STRING];
247
248     tmp = mutt_new_color_line ();
249     if (is_index) {
250       int i;
251
252       m_strcpy(buf, sizeof(buf), NONULL(s));
253       mutt_check_simple (buf, sizeof (buf), NONULL (SimpleSearch));
254       if ((tmp->color_pattern =
255            mutt_pattern_comp (buf, M_FULL_MSG, err)) == NULL) {
256         mutt_free_color_line(&tmp);
257         return -1;
258       }
259       /* force re-caching of index colors */
260       for (i = 0; Context && i < Context->msgcount; i++)
261         Context->hdrs[i]->pair = 0;
262     }
263     else
264       if ((r =
265            REGCOMP (&tmp->rx, s,
266                     (sensitive ? mutt_which_case (s) : REG_ICASE))) != 0) {
267       regerror (r, &tmp->rx, err->data, err->dsize);
268       mutt_free_color_line(&tmp);
269       return (-1);
270     }
271     tmp->next = *top;
272     tmp->pattern = m_strdup(s);
273     if (fg != -1 && bg != -1) {
274       tmp->fg = fg;
275       tmp->bg = bg;
276       attr |= madtty_color_pair(fg, bg);
277     }
278     tmp->pair = attr;
279     *top = tmp;
280   }
281
282   return 0;
283 }
284
285 static int
286 parse_object (BUFFER * buf, BUFFER * s, int *o, int *ql, BUFFER * err)
287 {
288   int q_level = 0;
289   char *eptr;
290
291   if (!MoreArgs (s)) {
292     m_strcpy(err->data, err->dsize, _("Missing arguments."));
293     return -1;
294   }
295
296   mutt_extract_token (buf, s, 0);
297   if (!m_strncmp(buf->data, "quoted", 6)) {
298     if (buf->data[6]) {
299       *ql = strtol (buf->data + 6, &eptr, 10);
300       if (*eptr || q_level < 0) {
301         snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
302         return -1;
303       }
304     }
305     else
306       *ql = 0;
307
308     *o = MT_COLOR_QUOTED;
309   }
310   else if ((*o = mutt_getvaluebyname (buf->data, Fields)) == -1) {
311     snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
312     return (-1);
313   }
314
315   return 0;
316 }
317
318 static int
319 parse_color_pair (BUFFER * buf, BUFFER * s, int *fg, int *bg, int *attr,
320                   BUFFER * err)
321 {
322   if (!MoreArgs (s)) {
323     m_strcpy(err->data, err->dsize, _("color: too few arguments"));
324     return (-1);
325   }
326
327   mutt_extract_token (buf, s, 0);
328
329   if (parse_color_name (buf->data, fg, attr, A_BOLD, err) != 0)
330     return (-1);
331
332   if (!MoreArgs (s)) {
333     m_strcpy(err->data, err->dsize, _("color: too few arguments"));
334     return (-1);
335   }
336
337   mutt_extract_token (buf, s, 0);
338
339   if (parse_color_name (buf->data, bg, attr, A_BLINK, err) != 0)
340     return (-1);
341
342   return 0;
343 }
344
345 #if 0
346 static int
347 parse_attr_spec (BUFFER * buf, BUFFER * s, int *fg, int *bg, int *attr,
348                  BUFFER * err)
349 {
350
351   if (fg)
352     *fg = -1;
353   if (bg)
354     *bg = -1;
355
356   if (!MoreArgs (s)) {
357     m_strcpy(err->data, err->dsize, _("mono: too few arguments"));
358     return (-1);
359   }
360
361   mutt_extract_token (buf, s, 0);
362
363   if (ascii_strcasecmp ("bold", buf->data) == 0)
364     *attr |= A_BOLD;
365   else if (ascii_strcasecmp ("underline", buf->data) == 0)
366     *attr |= A_UNDERLINE;
367   else if (ascii_strcasecmp ("none", buf->data) == 0)
368     *attr = A_NORMAL;
369   else if (ascii_strcasecmp ("reverse", buf->data) == 0)
370     *attr |= A_REVERSE;
371   else if (ascii_strcasecmp ("standout", buf->data) == 0)
372     *attr |= A_STANDOUT;
373   else if (ascii_strcasecmp ("normal", buf->data) == 0)
374     *attr = A_NORMAL;           /* needs use = instead of |= to clear other bits */
375   else {
376     snprintf (err->data, err->dsize, _("%s: no such attribute"), buf->data);
377     return (-1);
378   }
379
380   return 0;
381 }
382 #endif
383
384 static int fgbgattr_to_color (int fg, int bg, int attr)
385 {
386   if (fg != -1 && bg != -1)
387     return attr | madtty_color_pair(fg, bg);
388   else
389     return attr;
390 }
391
392 /* usage: color <object> <fg> <bg> [ <regexp> ] */
393 int mutt_parse_color(BUFFER *buf, BUFFER *s, unsigned long i, BUFFER *err)
394 {
395   int object = 0, attr = 0, fg = 0, bg = 0, q_level = 0;
396   int r = 0;
397
398   if (parse_object (buf, s, &object, &q_level, err) == -1)
399     return -1;
400
401   if (parse_color_pair(buf, s, &fg, &bg, &attr, err) == -1)
402     return -1;
403
404   /* extract a regular expression if needed */
405
406   if (object == MT_COLOR_HEADER || object == MT_COLOR_BODY
407       || object == MT_COLOR_INDEX) {
408     if (!MoreArgs (s)) {
409       m_strcpy(err->data, err->dsize, _("too few arguments"));
410       return (-1);
411     }
412
413     mutt_extract_token (buf, s, 0);
414   }
415
416   if (MoreArgs (s)) {
417     m_strcpy(err->data, err->dsize, _("too many arguments"));
418     return (-1);
419   }
420
421   if (option(OPTNOCURSES))
422     return 0;
423
424   /* delay use_default_colors() until needed, since it initializes things */
425   if (has_colors () && (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT)
426   &&  use_default_colors () != OK)
427   {
428     m_strcpy(err->data, err->dsize, _("default colors not supported"));
429     return (-1);
430   }
431
432   if (object == MT_COLOR_HEADER)
433     r = add_pattern (&ColorHdrList, buf->data, 0, fg, bg, attr, err, 0);
434   else if (object == MT_COLOR_BODY)
435     r = add_pattern (&ColorBodyList, buf->data, 1, fg, bg, attr, err, 0);
436   else if (object == MT_COLOR_INDEX) {
437     r = add_pattern (&ColorIndexList, buf->data, 1, fg, bg, attr, err, 1);
438     set_option (OPTFORCEREDRAWINDEX);
439   }
440   else if (object == MT_COLOR_QUOTED) {
441     if (q_level >= ColorQuoteSize) {
442       p_realloc(&ColorQuote, ColorQuoteSize += 2);
443       ColorQuote[ColorQuoteSize - 2] = ColorDefs[MT_COLOR_QUOTED];
444       ColorQuote[ColorQuoteSize - 1] = ColorDefs[MT_COLOR_QUOTED];
445     }
446     if (q_level >= ColorQuoteUsed)
447       ColorQuoteUsed = q_level + 1;
448     if (q_level == 0) {
449       ColorDefs[MT_COLOR_QUOTED] = fgbgattr_to_color (fg, bg, attr);
450
451       ColorQuote[0] = ColorDefs[MT_COLOR_QUOTED];
452       for (q_level = 1; q_level < ColorQuoteUsed; q_level++) {
453         if (ColorQuote[q_level] == A_NORMAL)
454           ColorQuote[q_level] = ColorDefs[MT_COLOR_QUOTED];
455       }
456     } else {
457       ColorQuote[q_level] = fgbgattr_to_color (fg, bg, attr);
458     }
459   } else {
460     ColorDefs[object] = fgbgattr_to_color (fg, bg, attr);
461   }
462
463   if (object == MT_COLOR_NORMAL)
464     BKGDSET(main_w, MT_COLOR_NORMAL);
465
466   return (r);
467 }