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