NeoMutt  2025-12-11-694-ga89709
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
draw.c
Go to the documentation of this file.
1
23
29
30#include "config.h"
31#include <stdbool.h>
32#include <string.h>
33#include <wchar.h>
34#include "mutt/lib.h"
35#include "config/lib.h"
36#include "email/lib.h"
37#include "core/lib.h"
38#include "gui/lib.h"
39#include "lib.h"
40#include "color/lib.h"
41#include "index/lib.h"
42#include "pattern/lib.h"
43
53static const struct AttrColor *get_color(int index, unsigned char *s)
54{
55 const int type = *s;
56 struct RegexColorList *rcl = regex_colors_get_list(type);
57 struct Mailbox *m_cur = get_current_mailbox();
58 struct Email *e = mutt_get_virt_email(m_cur, index);
59 if (!rcl || !e)
60 {
61 return simple_color_get(type);
62 }
63
64 struct RegexColor *np = NULL;
65 const struct AttrColor *ac_merge = NULL;
66
68 ASSERT(md);
69
70 struct Buffer *buf = buf_pool_get();
71 buf_strcpy(buf, (const char *) (s + 1));
72 char *marker = (char *) buf_find_char(buf, MUTT_SPECIAL_INDEX);
73 if (marker)
74 *marker = '\0';
75
76 // %Gx %{tags-transformed} - Transformed message tags
77 if (type == MT_COLOR_INDEX_TAG)
78 {
79 STAILQ_FOREACH(np, rcl, entries)
80 {
81 const char *transform = mutt_hash_find(md->tag_transforms, np->pattern);
82 if (transform && strstr(buf_string(buf), transform))
83
84 {
85 ac_merge = merged_color_overlay(ac_merge, &np->attr_color);
86 }
87 }
88 goto done;
89 }
90
91 // %g %{tags} / %J %{thread-tags} - Plain message tags
92 if (type == MT_COLOR_INDEX_TAGS)
93 {
94 STAILQ_FOREACH(np, rcl, entries)
95 {
96 if (strstr(buf_string(buf), np->pattern))
97 {
98 ac_merge = merged_color_overlay(ac_merge, &np->attr_color);
99 }
100 }
101 goto done;
102 }
103
104 STAILQ_FOREACH(np, rcl, entries)
105 {
107 MUTT_MATCH_FULL_ADDRESS, m_cur, e, NULL))
108 {
109 ac_merge = merged_color_overlay(ac_merge, &np->attr_color);
110 }
111 }
112
113done:
114 buf_pool_release(&buf);
115 return ac_merge;
116}
117
127static void print_enriched_string(struct MuttWindow *win, int index,
128 const struct AttrColor *ac_def, struct AttrColor *ac_ind,
129 struct Buffer *buf, struct ConfigSubset *sub)
130{
131 wchar_t wc = 0;
132 size_t k;
133 size_t n = mutt_str_len(buf_string(buf));
134 unsigned char *s = (unsigned char *) buf->data;
135 mbstate_t mbstate = { 0 };
136
137 const bool c_ascii_chars = cs_subset_bool(sub, "ascii_chars");
138 while (*s)
139 {
140 if (*s < MUTT_TREE_MAX)
141 {
142 /* Combining tree fg color and another bg color requires having
143 * use_default_colors, because the other bg color may be undefined. */
144 mutt_curses_set_color(ac_ind);
145
146 while (*s && (*s < MUTT_TREE_MAX))
147 {
148 switch (*s)
149 {
151 if (c_ascii_chars)
152 mutt_window_addch(win, '`');
153#ifdef WACS_LLCORNER
154 else
155 add_wch(WACS_LLCORNER);
156#else
157 else if (CharsetIsUtf8)
158 mutt_window_addstr(win, "\342\224\224"); /* WACS_LLCORNER */
159 else
160 mutt_window_addch(win, ACS_LLCORNER);
161#endif
162 break;
164 if (c_ascii_chars)
165 mutt_window_addch(win, ',');
166#ifdef WACS_ULCORNER
167 else
168 add_wch(WACS_ULCORNER);
169#else
170 else if (CharsetIsUtf8)
171 mutt_window_addstr(win, "\342\224\214"); /* WACS_ULCORNER */
172 else
173 mutt_window_addch(win, ACS_ULCORNER);
174#endif
175 break;
176 case MUTT_TREE_LTEE:
177 if (c_ascii_chars)
178 mutt_window_addch(win, '|');
179#ifdef WACS_LTEE
180 else
181 add_wch(WACS_LTEE);
182#else
183 else if (CharsetIsUtf8)
184 mutt_window_addstr(win, "\342\224\234"); /* WACS_LTEE */
185 else
186 mutt_window_addch(win, ACS_LTEE);
187#endif
188 break;
189 case MUTT_TREE_HLINE:
190 if (c_ascii_chars)
191 mutt_window_addch(win, '-');
192#ifdef WACS_HLINE
193 else
194 add_wch(WACS_HLINE);
195#else
196 else if (CharsetIsUtf8)
197 mutt_window_addstr(win, "\342\224\200"); /* WACS_HLINE */
198 else
199 mutt_window_addch(win, ACS_HLINE);
200#endif
201 break;
202 case MUTT_TREE_VLINE:
203 if (c_ascii_chars)
204 mutt_window_addch(win, '|');
205#ifdef WACS_VLINE
206 else
207 add_wch(WACS_VLINE);
208#else
209 else if (CharsetIsUtf8)
210 mutt_window_addstr(win, "\342\224\202"); /* WACS_VLINE */
211 else
212 mutt_window_addch(win, ACS_VLINE);
213#endif
214 break;
215 case MUTT_TREE_TTEE:
216 if (c_ascii_chars)
217 mutt_window_addch(win, '-');
218#ifdef WACS_TTEE
219 else
220 add_wch(WACS_TTEE);
221#else
222 else if (CharsetIsUtf8)
223 mutt_window_addstr(win, "\342\224\254"); /* WACS_TTEE */
224 else
225 mutt_window_addch(win, ACS_TTEE);
226#endif
227 break;
228 case MUTT_TREE_BTEE:
229 if (c_ascii_chars)
230 mutt_window_addch(win, '-');
231#ifdef WACS_BTEE
232 else
233 add_wch(WACS_BTEE);
234#else
235 else if (CharsetIsUtf8)
236 mutt_window_addstr(win, "\342\224\264"); /* WACS_BTEE */
237 else
238 mutt_window_addch(win, ACS_BTEE);
239#endif
240 break;
241 case MUTT_TREE_SPACE:
242 mutt_window_addch(win, ' ');
243 break;
244 case MUTT_TREE_RARROW:
245 mutt_window_addch(win, '>');
246 break;
247 case MUTT_TREE_STAR:
248 mutt_window_addch(win, '*'); /* fake thread indicator */
249 break;
250 case MUTT_TREE_HIDDEN:
251 mutt_window_addch(win, '&');
252 break;
253 case MUTT_TREE_EQUALS:
254 mutt_window_addch(win, '=');
255 break;
257 mutt_window_addch(win, '?');
258 break;
259 }
260 s++;
261 n--;
262 }
263 const struct AttrColor *ac_merge = merged_color_overlay(ac_def, ac_ind);
264 mutt_curses_set_color(ac_merge);
265 }
266 else if ((*s == MUTT_SPECIAL_INDEX) && (n >= 2))
267 {
268 s++;
269 if (*s == MT_COLOR_INDEX)
270 {
271 const struct AttrColor *ac_merge = merged_color_overlay(ac_def, ac_ind);
272 mutt_curses_set_color(ac_merge);
273 }
274 else
275 {
276 const struct AttrColor *color = get_color(index, s);
277 const struct AttrColor *ac_merge = merged_color_overlay(ac_def, color);
278 ac_merge = merged_color_overlay(ac_merge, ac_ind);
279
280 mutt_curses_set_color(ac_merge);
281 }
282 s++;
283 n -= 2;
284 }
285 else if ((k = mbrtowc(&wc, (char *) s, n, &mbstate)) > 0)
286 {
287 mutt_window_addnstr(win, (char *) s, k);
288 s += k;
289 n -= k;
290 }
291 else
292 {
293 break;
294 }
295 }
296}
297
305static void menu_pad_string(struct Menu *menu, struct Buffer *buf)
306{
307 int max_cols = menu->win->state.cols;
308 const bool c_arrow_cursor = cs_subset_bool(menu->sub, "arrow_cursor");
309 if (c_arrow_cursor)
310 {
311 const char *const c_arrow_string = cs_subset_string(menu->sub, "arrow_string");
312 if (max_cols > 0)
313 max_cols -= (mutt_strwidth(c_arrow_string) + 1);
314 }
315
316 int buf_cols = mutt_strwidth(buf_string(buf));
317 for (; buf_cols < max_cols; buf_cols++)
318 {
319 buf_addch(buf, ' ');
320 }
321}
322
327void menu_redraw_full(struct Menu *menu)
328{
330 mutt_window_clear(menu->win);
331
332 menu->page_len = menu->win->state.rows;
333
335}
336
341void menu_redraw_index(struct Menu *menu)
342{
343 struct Buffer *buf = buf_pool_get();
344 const struct AttrColor *ac = NULL;
345
346 const bool c_arrow_cursor = cs_subset_bool(menu->sub, "arrow_cursor");
347 const char *const c_arrow_string = cs_subset_string(menu->sub, "arrow_string");
348 const int arrow_width = mutt_strwidth(c_arrow_string);
349 struct AttrColor *ac_ind = menu->show_indicator ? simple_color_get(MT_COLOR_INDICATOR) : NULL;
350 for (int i = menu->top; i < (menu->top + menu->page_len); i++)
351 {
352 if (i < menu->max)
353 {
354 ac = menu->color(menu, i);
355
356 buf_reset(buf);
357 menu->make_entry(menu, i, menu->win->state.cols, buf);
358 menu_pad_string(menu, buf);
359
361 mutt_window_move(menu->win, i - menu->top, 0);
362
363 if (i == menu->current)
364 mutt_curses_set_color(ac_ind);
365
366 if (c_arrow_cursor)
367 {
368 if (i == menu->current)
369 {
370 mutt_window_addstr(menu->win, c_arrow_string);
372 mutt_window_addch(menu->win, ' ');
373 }
374 else
375 {
376 /* Print space chars to match the screen width of `$arrow_string` */
377 mutt_window_printf(menu->win, "%*s", arrow_width + 1, "");
378 }
379 }
380
381 if ((i == menu->current) && !c_arrow_cursor)
382 {
383 print_enriched_string(menu->win, i, ac, ac_ind, buf, menu->sub);
384 }
385 else
386 {
387 print_enriched_string(menu->win, i, ac, NULL, buf, menu->sub);
388 }
389 }
390 else
391 {
393 mutt_window_clearline(menu->win, i - menu->top);
394 }
395 }
398 buf_pool_release(&buf);
399}
400
405void menu_redraw_motion(struct Menu *menu)
406{
407 struct Buffer *buf = buf_pool_get();
408
409 /* Note: menu->color() for the index can end up retrieving a message
410 * over imap (if matching against ~h for instance). This can
411 * generate status messages. So we want to call it *before* we
412 * position the cursor for drawing. */
413 const struct AttrColor *old_color = menu->color(menu, menu->old_current);
414 mutt_window_move(menu->win, menu->old_current - menu->top, 0);
415 mutt_curses_set_color(old_color);
416
417 const bool c_arrow_cursor = cs_subset_bool(menu->sub, "arrow_cursor");
418 struct AttrColor *ac_ind = menu->show_indicator ? simple_color_get(MT_COLOR_INDICATOR) : NULL;
419 if (c_arrow_cursor)
420 {
421 const char *const c_arrow_string = cs_subset_string(menu->sub, "arrow_string");
422 const int arrow_width = mutt_strwidth(c_arrow_string);
423 /* clear the arrow */
424 /* Print space chars to match the screen width of `$arrow_string` */
425 mutt_window_printf(menu->win, "%*s", arrow_width + 1, "");
427
428 menu->make_entry(menu, menu->old_current, menu->win->state.cols, buf);
429 menu_pad_string(menu, buf);
430 mutt_window_move(menu->win, menu->old_current - menu->top, arrow_width + 1);
431 print_enriched_string(menu->win, menu->old_current, old_color, NULL, buf, menu->sub);
432
433 /* now draw it in the new location */
434 mutt_curses_set_color(ac_ind);
435 mutt_window_move(menu->win, menu->current - menu->top, 0);
436 mutt_window_addstr(menu->win, c_arrow_string);
437 }
438 else
439 {
441 /* erase the current indicator */
442 menu->make_entry(menu, menu->old_current, menu->win->state.cols, buf);
443 menu_pad_string(menu, buf);
444 print_enriched_string(menu->win, menu->old_current, old_color, NULL, buf, menu->sub);
445
446 /* now draw the new one to reflect the change */
447 const struct AttrColor *cur_color = menu->color(menu, menu->current);
448 cur_color = merged_color_overlay(cur_color, ac_ind);
449 buf_reset(buf);
450 menu->make_entry(menu, menu->current, menu->win->state.cols, buf);
451 menu_pad_string(menu, buf);
452 mutt_window_move(menu->win, menu->current - menu->top, 0);
453 mutt_curses_set_color(cur_color);
454 print_enriched_string(menu->win, menu->current, cur_color, ac_ind, buf, menu->sub);
455 }
457 buf_pool_release(&buf);
458}
459
464void menu_redraw_current(struct Menu *menu)
465{
466 struct Buffer *buf = buf_pool_get();
467 const struct AttrColor *ac = menu->color(menu, menu->current);
468
469 mutt_window_move(menu->win, menu->current - menu->top, 0);
470 menu->make_entry(menu, menu->current, menu->win->state.cols, buf);
471 menu_pad_string(menu, buf);
472
473 struct AttrColor *ac_ind = menu->show_indicator ? simple_color_get(MT_COLOR_INDICATOR) : NULL;
474 const bool c_arrow_cursor = cs_subset_bool(menu->sub, "arrow_cursor");
475 if (c_arrow_cursor)
476 {
477 mutt_curses_set_color(ac_ind);
478 const char *const c_arrow_string = cs_subset_string(menu->sub, "arrow_string");
479 mutt_window_addstr(menu->win, c_arrow_string);
481 mutt_window_addch(menu->win, ' ');
482 menu_pad_string(menu, buf);
483 print_enriched_string(menu->win, menu->current, ac, NULL, buf, menu->sub);
484 }
485 else
486 {
487 print_enriched_string(menu->win, menu->current, ac, ac_ind, buf, menu->sub);
488 }
490 buf_pool_release(&buf);
491}
492
499int menu_redraw(struct Menu *menu)
500{
501 /* See if all or part of the screen needs to be updated. */
502 if (menu->redraw & MENU_REDRAW_FULL)
503 menu_redraw_full(menu);
504
505 if (menu->redraw & MENU_REDRAW_INDEX)
506 menu_redraw_index(menu);
507 else if (menu->redraw & MENU_REDRAW_MOTION)
508 menu_redraw_motion(menu);
509 else if (menu->redraw == MENU_REDRAW_CURRENT)
511
512 return OP_NULL;
513}
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:241
const char * buf_find_char(const struct Buffer *buf, const char c)
Return a pointer to a char found in the buffer.
Definition buffer.c:653
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
Color and attribute parsing.
struct RegexColorList * regex_colors_get_list(enum ColorId cid)
Return the RegexColorList for a Colour ID.
Definition regex.c:205
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition simple.c:95
@ MT_COLOR_INDICATOR
Selected item in list.
Definition color.h:49
@ MT_COLOR_INDEX_TAGS
Index: tags field (g, J)
Definition color.h:96
@ MT_COLOR_INDEX_TAG
Index: tag field (G)
Definition color.h:95
@ MT_COLOR_NORMAL
Plain text.
Definition color.h:53
@ MT_COLOR_INDEX
Index: default colour.
Definition color.h:86
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
size_t mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition curs_lib.c:444
void menu_redraw_current(struct Menu *menu)
Redraw the current menu.
Definition draw.c:464
static const struct AttrColor * get_color(int index, unsigned char *s)
Choose a colour for a line of the index.
Definition draw.c:53
void menu_redraw_index(struct Menu *menu)
Force the redraw of the index.
Definition draw.c:341
int menu_redraw(struct Menu *menu)
Redraw the parts of the screen that have been flagged to be redrawn.
Definition draw.c:499
void menu_redraw_full(struct Menu *menu)
Force the redraw of the Menu.
Definition draw.c:327
static void menu_pad_string(struct Menu *menu, struct Buffer *buf)
Pad a string with spaces for display in the Menu.
Definition draw.c:305
static void print_enriched_string(struct MuttWindow *win, int index, const struct AttrColor *ac_def, struct AttrColor *ac_ind, struct Buffer *buf, struct ConfigSubset *sub)
Display a string with embedded colours and graphics.
Definition draw.c:127
void menu_redraw_motion(struct Menu *menu)
Force the redraw of the list part of the menu.
Definition draw.c:405
Structs that make up an email.
Convenience wrapper for the gui headers.
@ MUTT_TREE_MAX
Definition thread.h:70
@ MUTT_TREE_LLCORNER
Lower left corner.
Definition thread.h:57
@ MUTT_TREE_RARROW
Right arrow.
Definition thread.h:63
@ MUTT_SPECIAL_INDEX
Colour indicator.
Definition thread.h:72
@ MUTT_TREE_ULCORNER
Upper left corner.
Definition thread.h:58
@ MUTT_TREE_EQUALS
Equals (for threads)
Definition thread.h:66
@ MUTT_TREE_HIDDEN
Ampersand character (for threads)
Definition thread.h:65
@ MUTT_TREE_STAR
Star character (for threads)
Definition thread.h:64
@ MUTT_TREE_LTEE
Left T-piece.
Definition thread.h:59
@ MUTT_TREE_VLINE
Vertical line.
Definition thread.h:61
@ MUTT_TREE_MISSING
Question mark.
Definition thread.h:69
@ MUTT_TREE_TTEE
Top T-piece.
Definition thread.h:67
@ MUTT_TREE_HLINE
Horizontal line.
Definition thread.h:60
@ MUTT_TREE_SPACE
Blank space.
Definition thread.h:62
@ MUTT_TREE_BTEE
Bottom T-piece.
Definition thread.h:68
void * mutt_hash_find(const struct HashTable *table, const char *strkey)
Find the HashElem data in a Hash Table element using a key.
Definition hash.c:364
GUI manage the main index (list of emails)
struct Mailbox * get_current_mailbox(void)
Get the current Mailbox.
Definition index.c:721
GUI present the user with a selectable list.
#define MENU_REDRAW_FULL
Redraw everything.
Definition lib.h:60
#define MENU_REDRAW_INDEX
Redraw the index.
Definition lib.h:57
#define MENU_REDRAW_NO_FLAGS
No flags are set.
Definition lib.h:56
#define MENU_REDRAW_CURRENT
Redraw the current line of the menu.
Definition lib.h:59
#define MENU_REDRAW_MOTION
Redraw after moving the menu list.
Definition lib.h:58
const struct AttrColor * merged_color_overlay(const struct AttrColor *base, const struct AttrColor *over)
Combine two colours.
Definition merged.c:107
@ MODULE_ID_EMAIL
ModuleEmail, Email code
Definition module_api.h:64
bool CharsetIsUtf8
Is the user's current character set utf-8?
Definition charset.c:66
Convenience wrapper for the library headers.
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
const struct AttrColor * mutt_curses_set_color_by_id(enum ColorId cid)
Set the colour and attributes by the Colour ID.
Definition mutt_curses.c:79
void mutt_curses_set_color(const struct AttrColor *ac)
Set the colour and attributes for text.
Definition mutt_curses.c:38
void mutt_window_clear(struct MuttWindow *win)
Clear a Window.
int mutt_window_printf(struct MuttWindow *win, const char *fmt,...)
Write a formatted string to a Window.
int mutt_window_move(struct MuttWindow *win, int row, int col)
Move the cursor in a Window.
void mutt_window_clearline(struct MuttWindow *win, int row)
Clear a row of a Window.
int mutt_window_addstr(struct MuttWindow *win, const char *str)
Write a string to a Window.
int mutt_window_addnstr(struct MuttWindow *win, const char *str, int num)
Write a partial string to a Window.
int mutt_window_addch(struct MuttWindow *win, int ch)
Write one character to a Window.
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition mview.c:415
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:585
bool mutt_pattern_exec(struct Pattern *pat, PatternExecFlags flags, struct Mailbox *m, struct Email *e, struct PatternCache *cache)
Match a pattern against an email header.
Definition exec.c:1151
Match patterns to emails.
#define MUTT_MATCH_FULL_ADDRESS
Match the full address.
Definition lib.h:107
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
#define SLIST_FIRST(head)
Definition queue.h:227
#define ASSERT(COND)
Definition signal2.h:59
A curses colour and its attributes.
Definition attr.h:65
String manipulation buffer.
Definition buffer.h:36
char * data
Pointer to data.
Definition buffer.h:37
A set of inherited config items.
Definition subset.h:46
Email private Module data.
Definition module_data.h:32
struct HashTable * tag_transforms
Hash Table: "inbox" -> "i" - Alternative tag names.
Definition module_data.h:44
The envelope/body of an email.
Definition email.h:39
int index
The absolute (unsorted) message number.
Definition email.h:110
A mailbox.
Definition mailbox.h:78
Definition lib.h:80
struct MuttWindow * win
Window holding the Menu.
Definition lib.h:88
int current
Current entry.
Definition lib.h:81
const struct AttrColor *(* color)(struct Menu *menu, int line)
Definition lib.h:145
MenuRedrawFlags redraw
When to redraw the screen.
Definition lib.h:83
bool show_indicator
Show the Indicator colour.
Definition lib.h:87
int top
Entry that is the top of the current page.
Definition lib.h:92
int(* make_entry)(struct Menu *menu, int line, int max_cols, struct Buffer *buf)
Definition lib.h:108
struct ConfigSubset * sub
Inherited config items.
Definition lib.h:89
int page_len
Number of entries per screen.
Definition lib.h:85
int old_current
For driver use only.
Definition lib.h:93
struct WindowState state
Current state of the Window.
Container for Accounts, Notifications.
Definition neomutt.h:41
A regular expression and a color to highlight a line.
Definition regex4.h:35
struct PatternList * color_pattern
Compiled pattern to speed up index color calculation.
Definition regex4.h:40
struct AttrColor attr_color
Colour and attributes to apply.
Definition regex4.h:36
char * pattern
Pattern to match.
Definition regex4.h:37
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition mutt_window.h:60
short rows
Number of rows, can be MUTT_WIN_SIZE_UNLIMITED.
Definition mutt_window.h:61